#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Xml.Serialization;
using AbstractClass.Linq;

namespace AbstractClass.Collections.Generic
{
    /// <summary>
    /// Represents an abstract tree node data structure.
    /// </summary>
    /// <typeparam name="T">The type of the value in the node.</typeparam>
    [Serializable, DebuggerDisplay("Value = {Value}")]
    public abstract class AbstractNode<T> : ISerializable
    {
        [NonSerialized] private AbstractNode<T> _parent;

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractNode&lt;T&gt;"/> class.
        /// </summary>
        protected AbstractNode()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractNode&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="data">The value corresponding to this instance.</param>
        protected AbstractNode(T data)
        {
            Value = data;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractNode&lt;T&gt;"/> class.
        /// </summary>
        /// <param name = "info">The <see cref = "T:System.Runtime.Serialization.SerializationInfo" /> to populate with data. 
        /// </param>
        /// <param name = "context">The destination (see <see cref = "T:System.Runtime.Serialization.StreamingContext" />) for this serialization. 
        /// </param>
        protected AbstractNode(SerializationInfo info, StreamingContext context)
        {
        }

        /// <summary>
        /// Gets or sets the parent node of this instance.
        /// </summary>
        /// <value>The parent node.</value>
        [XmlIgnore]
        public AbstractNode<T> Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is a leaf node.
        /// </summary>
        /// <value><c>true</c> if this instance is leaf node; otherwise, <c>false</c>.</value>
        public abstract bool IsLeaf { get; }

        /// <summary>
        /// Gets the child node count of the node.
        /// </summary>
        /// <value>The child node count.</value>
        public abstract int ChildCount { get; }

        /// <summary>
        /// Gets or sets the value corresponding to this instance.
        /// </summary>
        /// <value>The value.</value>
        public T Value { get; set; }

        /// <summary>
        /// Gets or sets the depth of the current instance in the tree.
        /// </summary>
        /// <value>The depth.</value>
        public int Depth { get; protected internal set; }

        /// <summary>
        /// Gets the path to root node from the current instance.
        /// </summary>
        /// <value>The path to root as a collection of adjacent nodes.</value>
        public virtual IEnumerable<AbstractNode<T>> PathToRoot
        {
            get
            {
                yield return this;
                AbstractNode<T> parent = Parent;
                while (parent != null)
                {
                    yield return parent;
                    parent = parent.Parent;
                }
            }
        }

        /// <summary>
        /// Determines whether the current instance is equal to <paramref name="other"/> or not.
        /// </summary>
        /// <param name="other">The other node.</param>
        /// <returns>
        /// <c>true</c> if this instance is equal to <paramref name="other" />; otherwise, <c>false</c>.
        /// </returns>
        public abstract bool Equals(AbstractNode<T> other);

        /// <summary>
        /// Determines whether this instance is root.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance is root; otherwise, <c>false</c>.
        /// </returns>
        public bool IsRoot()
        {
            return Parent == null;
        }

        /// <summary>
        /// Gets the path to <paramref name="destinationNode"/> from the current instance.
        /// </summary>
        /// <param name="destinationNode">The destination node.</param>
        /// <returns>The path to <paramref name="destinationNode"/> as a collection of adjacent nodes.</returns>
        public virtual IEnumerable<AbstractNode<T>> GetPath(AbstractNode<T> destinationNode)
        {
            IEnumerable<AbstractNode<T>> sourceToRoot = PathToRoot;
            IEnumerable<AbstractNode<T>> destinationToRoot = destinationNode.PathToRoot;

            return (from node in sourceToRoot
                    where destinationToRoot.Contains(node)
                    let sliceOne = destinationToRoot.TakeUpto(node)
                    let sliceTwo = sourceToRoot.TakeUpto(node).Except(node)
                    select sliceTwo.Concat(sliceOne.Reverse())).FirstOrDefault();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("{0}", Value);
        }

        #region Implementation of ISerializable

        /// <summary>
        ///   Populates a <see cref = "T:System.Runtime.Serialization.SerializationInfo" /> with the data needed to serialize the target object.
        /// </summary>
        /// <param name = "info">The <see cref = "T:System.Runtime.Serialization.SerializationInfo" /> to populate with data. 
        /// </param>
        /// <param name = "context">The destination (see <see cref = "T:System.Runtime.Serialization.StreamingContext" />) for this serialization. 
        /// </param>
        /// <exception cref = "T:System.Security.SecurityException">The caller does not have the required permission. 
        /// </exception>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        public abstract void GetObjectData(SerializationInfo info, StreamingContext context);

        #endregion
    }
}